﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using IPC.Communication.BasicFramework;
using IPC.Communication.Core;
using IPC.Communication.Core.Net;
using IPC.Communication.Reflection;

namespace IPC.Communication.Profinet.Melsec
{
    public class MelsecFxLinksOverTcp : NetworkDeviceBase
    {
        private byte station = 0;

        private byte watiingTime = 0;

        private bool sumCheck = true;

        public byte Station
        {
            get
            {
                return station;
            }
            set
            {
                station = value;
            }
        }

        public byte WaittingTime
        {
            get
            {
                return watiingTime;
            }
            set
            {
                if (watiingTime > 15)
                {
                    watiingTime = 15;
                }
                else
                {
                    watiingTime = value;
                }
            }
        }

        public bool SumCheck
        {
            get
            {
                return sumCheck;
            }
            set
            {
                sumCheck = value;
            }
        }

        public MelsecFxLinksOverTcp()
        {
            base.WordLength = 1;
            base.ByteTransform = new RegularByteTransform();
            base.SleepTime = 20;
        }

        public MelsecFxLinksOverTcp(string ipAddress, int port)
            : this()
        {
            IpAddress = ipAddress;
            Port = port;
        }

        [HslMqttApi("ReadByteArray", "Read PLC data in batches, in units of words, supports reading X, Y, M, S, D, T, C.")]
        public override OperateResult<byte[]> Read(string address, ushort length)
        {
            byte b = (byte)HslHelper.ExtractParameter(ref address, "s", station);
            bool isBool = false;
            OperateResult<byte[]> operateResult = BuildReadCommand(b, address, length, isBool = false, sumCheck, watiingTime);
            if (!operateResult.IsSuccess)
            {
                return OperateResult.CreateFailedResult<byte[]>(operateResult);
            }
            OperateResult<byte[]> operateResult2 = ReadFromCoreServer(operateResult.Content);
            if (!operateResult2.IsSuccess)
            {
                return OperateResult.CreateFailedResult<byte[]>(operateResult2);
            }
            if (operateResult2.Content[0] != 2)
            {
                return new OperateResult<byte[]>(operateResult2.Content[0], "Read Faild:" + SoftBasic.ByteToHexString(operateResult2.Content, ' '));
            }
            byte[] array = new byte[length * 2];
            for (int i = 0; i < array.Length / 2; i++)
            {
                ushort value = Convert.ToUInt16(Encoding.ASCII.GetString(operateResult2.Content, i * 4 + 5, 4), 16);
                BitConverter.GetBytes(value).CopyTo(array, i * 2);
            }
            return OperateResult.CreateSuccessResult(array);
        }

        [HslMqttApi("WriteByteArray", "The data written to the PLC in batches is in units of words, that is, at least 2 bytes of information. It supports X, Y, M, S, D, T, and C. ")]
        public override OperateResult Write(string address, byte[] value)
        {
            byte b = (byte)HslHelper.ExtractParameter(ref address, "s", station);
            OperateResult<byte[]> operateResult = BuildWriteByteCommand(b, address, value, sumCheck, watiingTime);
            if (!operateResult.IsSuccess)
            {
                return operateResult;
            }
            OperateResult<byte[]> operateResult2 = ReadFromCoreServer(operateResult.Content);
            if (!operateResult2.IsSuccess)
            {
                return operateResult2;
            }
            if (operateResult2.Content[0] != 6)
            {
                return new OperateResult(operateResult2.Content[0], "Write Faild:" + SoftBasic.ByteToHexString(operateResult2.Content, ' '));
            }
            return OperateResult.CreateSuccessResult();
        }

        //[AsyncStateMachine(typeof(<ReadAsync>d__13))]
        //[DebuggerStepThrough]
        //public override Task<OperateResult<byte[]>> ReadAsync(string address, ushort length)
        //{
        //    <ReadAsync>d__13 stateMachine = new <ReadAsync>d__13();
        //    stateMachine.<>t__builder = AsyncTaskMethodBuilder<OperateResult<byte[]>>.Create();
        //    stateMachine.<>4__this = this;
        //    stateMachine.address = address;
        //    stateMachine.length = length;
        //    stateMachine.<>1__state = -1;
        //    stateMachine.<>t__builder.Start(ref stateMachine);
        //    return stateMachine.<>t__builder.Task;
        //}

        //[AsyncStateMachine(typeof(<WriteAsync>d__14))]
        //[DebuggerStepThrough]
        //public override Task<OperateResult> WriteAsync(string address, byte[] value)
        //{
        //    <WriteAsync>d__14 stateMachine = new <WriteAsync>d__14();
        //    stateMachine.<>t__builder = AsyncTaskMethodBuilder<OperateResult>.Create();
        //    stateMachine.<>4__this = this;
        //    stateMachine.address = address;
        //    stateMachine.value = value;
        //    stateMachine.<>1__state = -1;
        //    stateMachine.<>t__builder.Start(ref stateMachine);
        //    return stateMachine.<>t__builder.Task;
        //}

        [HslMqttApi("ReadBoolArray", "Read bool data in batches. The supported types are X, Y, S, T, C.")]
        public override OperateResult<bool[]> ReadBool(string address, ushort length)
        {
            byte b = (byte)HslHelper.ExtractParameter(ref address, "s", station);
            bool isBool = true;
            OperateResult<byte[]> operateResult = BuildReadCommand(b, address, length, isBool = true, sumCheck, watiingTime);
            if (!operateResult.IsSuccess)
            {
                return OperateResult.CreateFailedResult<bool[]>(operateResult);
            }
            OperateResult<byte[]> operateResult2 = ReadFromCoreServer(operateResult.Content);
            if (!operateResult2.IsSuccess)
            {
                return OperateResult.CreateFailedResult<bool[]>(operateResult2);
            }
            if (operateResult2.Content[0] != 2)
            {
                return new OperateResult<bool[]>(operateResult2.Content[0], "Read Faild:" + SoftBasic.ByteToHexString(operateResult2.Content, ' '));
            }
            byte[] array = new byte[length];
            Array.Copy(operateResult2.Content, 5, array, 0, length);
            return OperateResult.CreateSuccessResult(array.Select((byte m) => m == 49).ToArray());
        }

        [HslMqttApi("WriteBoolArray", "Write arrays of type bool in batches. The supported types are X, Y, S, T, C.")]
        public override OperateResult Write(string address, bool[] value)
        {
            byte b = (byte)HslHelper.ExtractParameter(ref address, "s", station);
            OperateResult<byte[]> operateResult = BuildWriteBoolCommand(b, address, value, sumCheck, watiingTime);
            if (!operateResult.IsSuccess)
            {
                return operateResult;
            }
            OperateResult<byte[]> operateResult2 = ReadFromCoreServer(operateResult.Content);
            if (!operateResult2.IsSuccess)
            {
                return operateResult2;
            }
            if (operateResult2.Content[0] != 6)
            {
                return new OperateResult(operateResult2.Content[0], "Write Faild:" + SoftBasic.ByteToHexString(operateResult2.Content, ' '));
            }
            return OperateResult.CreateSuccessResult();
        }

        //[AsyncStateMachine(typeof(<ReadBoolAsync>d__17))]
        //[DebuggerStepThrough]
        //public override Task<OperateResult<bool[]>> ReadBoolAsync(string address, ushort length)
        //{
        //    <ReadBoolAsync>d__17 stateMachine = new <ReadBoolAsync>d__17();
        //    stateMachine.<>t__builder = AsyncTaskMethodBuilder<OperateResult<bool[]>>.Create();
        //    stateMachine.<>4__this = this;
        //    stateMachine.address = address;
        //    stateMachine.length = length;
        //    stateMachine.<>1__state = -1;
        //    stateMachine.<>t__builder.Start(ref stateMachine);
        //    return stateMachine.<>t__builder.Task;
        //}

        //[AsyncStateMachine(typeof(<WriteAsync>d__18))]
        //[DebuggerStepThrough]
        //public override Task<OperateResult> WriteAsync(string address, bool[] value)
        //{
        //    <WriteAsync>d__18 stateMachine = new <WriteAsync>d__18();
        //    stateMachine.<>t__builder = AsyncTaskMethodBuilder<OperateResult>.Create();
        //    stateMachine.<>4__this = this;
        //    stateMachine.address = address;
        //    stateMachine.value = value;
        //    stateMachine.<>1__state = -1;
        //    stateMachine.<>t__builder.Start(ref stateMachine);
        //    return stateMachine.<>t__builder.Task;
        //}

        [HslMqttApi(Description = "Start the PLC operation, you can carry additional parameter information and specify the station number. Example: s=2; Note: The semicolon is required.")]
        public OperateResult StartPLC(string parameter = "")
        {
            byte b = (byte)HslHelper.ExtractParameter(ref parameter, "s", station);
            OperateResult<byte[]> operateResult = BuildStart(b, sumCheck, watiingTime);
            if (!operateResult.IsSuccess)
            {
                return operateResult;
            }
            OperateResult<byte[]> operateResult2 = ReadFromCoreServer(operateResult.Content);
            if (!operateResult2.IsSuccess)
            {
                return operateResult2;
            }
            if (operateResult2.Content[0] != 6)
            {
                return new OperateResult(operateResult2.Content[0], "Start Faild:" + SoftBasic.ByteToHexString(operateResult2.Content, ' '));
            }
            return OperateResult.CreateSuccessResult();
        }

        [HslMqttApi(Description = "Stop PLC operation, you can carry additional parameter information and specify the station number. Example: s=2; Note: The semicolon is required.")]
        public OperateResult StopPLC(string parameter = "")
        {
            byte b = (byte)HslHelper.ExtractParameter(ref parameter, "s", station);
            OperateResult<byte[]> operateResult = BuildStop(b, sumCheck, watiingTime);
            if (!operateResult.IsSuccess)
            {
                return operateResult;
            }
            OperateResult<byte[]> operateResult2 = ReadFromCoreServer(operateResult.Content);
            if (!operateResult2.IsSuccess)
            {
                return operateResult2;
            }
            if (operateResult2.Content[0] != 6)
            {
                return new OperateResult(operateResult2.Content[0], "Stop Faild:" + SoftBasic.ByteToHexString(operateResult2.Content, ' '));
            }
            return OperateResult.CreateSuccessResult();
        }

        [HslMqttApi(Description = "Read the PLC model information, you can carry additional parameter information, and specify the station number. Example: s=2; Note: The semicolon is required.")]
        public OperateResult<string> ReadPlcType(string parameter = "")
        {
            byte b = (byte)HslHelper.ExtractParameter(ref parameter, "s", station);
            OperateResult<byte[]> operateResult = BuildReadPlcType(b, sumCheck, watiingTime);
            if (!operateResult.IsSuccess)
            {
                return OperateResult.CreateFailedResult<string>(operateResult);
            }
            OperateResult<byte[]> operateResult2 = ReadFromCoreServer(operateResult.Content);
            if (!operateResult2.IsSuccess)
            {
                return OperateResult.CreateFailedResult<string>(operateResult2);
            }
            if (operateResult2.Content[0] != 6)
            {
                return new OperateResult<string>(operateResult2.Content[0], "ReadPlcType Faild:" + SoftBasic.ByteToHexString(operateResult2.Content, ' '));
            }
            return GetPlcTypeFromCode(Encoding.ASCII.GetString(operateResult2.Content, 5, 2));
        }

        //[AsyncStateMachine(typeof(<StartPLCAsync>d__22))]
        //[DebuggerStepThrough]
        //public Task<OperateResult> StartPLCAsync(string parameter = "")
        //{
        //    <StartPLCAsync>d__22 stateMachine = new <StartPLCAsync>d__22();
        //    stateMachine.<>t__builder = AsyncTaskMethodBuilder<OperateResult>.Create();
        //    stateMachine.<>4__this = this;
        //    stateMachine.parameter = parameter;
        //    stateMachine.<>1__state = -1;
        //    stateMachine.<>t__builder.Start(ref stateMachine);
        //    return stateMachine.<>t__builder.Task;
        //}

        //[AsyncStateMachine(typeof(<StopPLCAsync>d__23))]
        //[DebuggerStepThrough]
        //public Task<OperateResult> StopPLCAsync(string parameter = "")
        //{
        //    <StopPLCAsync>d__23 stateMachine = new <StopPLCAsync>d__23();
        //    stateMachine.<>t__builder = AsyncTaskMethodBuilder<OperateResult>.Create();
        //    stateMachine.<>4__this = this;
        //    stateMachine.parameter = parameter;
        //    stateMachine.<>1__state = -1;
        //    stateMachine.<>t__builder.Start(ref stateMachine);
        //    return stateMachine.<>t__builder.Task;
        //}

        //[AsyncStateMachine(typeof(<ReadPlcTypeAsync>d__24))]
        //[DebuggerStepThrough]
        //public Task<OperateResult<string>> ReadPlcTypeAsync(string parameter = "")
        //{
        //    <ReadPlcTypeAsync>d__24 stateMachine = new <ReadPlcTypeAsync>d__24();
        //    stateMachine.<>t__builder = AsyncTaskMethodBuilder<OperateResult<string>>.Create();
        //    stateMachine.<>4__this = this;
        //    stateMachine.parameter = parameter;
        //    stateMachine.<>1__state = -1;
        //    stateMachine.<>t__builder.Start(ref stateMachine);
        //    return stateMachine.<>t__builder.Task;
        //}

        public override string ToString()
        {
            return String.Format("MelsecFxLinksOverTcp[{0}:{1}]", IpAddress, Port);
        }

        private static OperateResult<string> FxAnalysisAddress(string address)
        {
            OperateResult<string> operateResult = new OperateResult<string>();
            try
            {
                switch (address[0])
                {
                    case 'X':
                    case 'x':
                        {
                            ushort num2 = Convert.ToUInt16(address.Substring(1), 8);
                            operateResult.Content = "X" + Convert.ToUInt16(address.Substring(1), 10).ToString("D4");
                            break;
                        }
                    case 'Y':
                    case 'y':
                        {
                            ushort num = Convert.ToUInt16(address.Substring(1), 8);
                            operateResult.Content = "Y" + Convert.ToUInt16(address.Substring(1), 10).ToString("D4");
                            break;
                        }
                    case 'M':
                    case 'm':
                        operateResult.Content = "M" + Convert.ToUInt16(address.Substring(1), 10).ToString("D4");
                        break;
                    case 'S':
                    case 's':
                        operateResult.Content = "S" + Convert.ToUInt16(address.Substring(1), 10).ToString("D4");
                        break;
                    case 'T':
                    case 't':
                        if (address[1] == 'S' || address[1] == 's')
                        {
                            operateResult.Content = "TS" + Convert.ToUInt16(address.Substring(1), 10).ToString("D3");
                        }
                        else
                        {
                            if (address[1] != 'N' && address[1] != 'n')
                            {
                                throw new Exception(StringResources.Language.NotSupportedDataType);
                            }
                            operateResult.Content = "TN" + Convert.ToUInt16(address.Substring(1), 10).ToString("D3");
                        }
                        break;
                    case 'C':
                    case 'c':
                        if (address[1] == 'S' || address[1] == 's')
                        {
                            operateResult.Content = "CS" + Convert.ToUInt16(address.Substring(1), 10).ToString("D3");
                        }
                        else
                        {
                            if (address[1] != 'N' && address[1] != 'n')
                            {
                                throw new Exception(StringResources.Language.NotSupportedDataType);
                            }
                            operateResult.Content = "CN" + Convert.ToUInt16(address.Substring(1), 10).ToString("D3");
                        }
                        break;
                    case 'D':
                    case 'd':
                        operateResult.Content = "D" + Convert.ToUInt16(address.Substring(1), 10).ToString("D4");
                        break;
                    case 'R':
                    case 'r':
                        operateResult.Content = "R" + Convert.ToUInt16(address.Substring(1), 10).ToString("D4");
                        break;
                    default:
                        throw new Exception(StringResources.Language.NotSupportedDataType);
                }
            }
            catch (Exception ex)
            {
                operateResult.Message = ex.Message;
                return operateResult;
            }
            operateResult.IsSuccess = true;
            return operateResult;
        }

        public static string CalculateAcc(string data)
        {
            byte[] bytes = Encoding.ASCII.GetBytes(data);
            int num = 0;
            for (int i = 0; i < bytes.Length; i++)
            {
                num += bytes[i];
            }
            return data + num.ToString("X4").Substring(2);
        }

        public static OperateResult<byte[]> BuildReadCommand(byte station, string address, ushort length, bool isBool, bool sumCheck = true, byte waitTime = 0)
        {
            OperateResult<string> operateResult = FxAnalysisAddress(address);
            if (!operateResult.IsSuccess)
            {
                return OperateResult.CreateFailedResult<byte[]>(operateResult);
            }
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append(station.ToString("D2"));
            stringBuilder.Append("FF");
            if (isBool)
            {
                stringBuilder.Append("BR");
            }
            else
            {
                stringBuilder.Append("WR");
            }
            stringBuilder.Append(waitTime.ToString("X"));
            stringBuilder.Append(operateResult.Content);
            stringBuilder.Append(length.ToString("D2"));
            byte[] array = null;
            array = ((!sumCheck) ? Encoding.ASCII.GetBytes(stringBuilder.ToString()) : Encoding.ASCII.GetBytes(CalculateAcc(stringBuilder.ToString())));
            array = SoftBasic.SpliceArray<byte>(new byte[1]
		{
			5
		}, array);
            return OperateResult.CreateSuccessResult(array);
        }

        public static OperateResult<byte[]> BuildWriteBoolCommand(byte station, string address, bool[] value, bool sumCheck = true, byte waitTime = 0)
        {
            OperateResult<string> operateResult = FxAnalysisAddress(address);
            if (!operateResult.IsSuccess)
            {
                return OperateResult.CreateFailedResult<byte[]>(operateResult);
            }
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append(station.ToString("D2"));
            stringBuilder.Append("FF");
            stringBuilder.Append("BW");
            stringBuilder.Append(waitTime.ToString("X"));
            stringBuilder.Append(operateResult.Content);
            stringBuilder.Append(value.Length.ToString("D2"));
            for (int i = 0; i < value.Length; i++)
            {
                stringBuilder.Append(value[i] ? "1" : "0");
            }
            byte[] array = null;
            array = ((!sumCheck) ? Encoding.ASCII.GetBytes(stringBuilder.ToString()) : Encoding.ASCII.GetBytes(CalculateAcc(stringBuilder.ToString())));
            array = SoftBasic.SpliceArray<byte>(new byte[1]
		{
			5
		}, array);
            return OperateResult.CreateSuccessResult(array);
        }

        public static OperateResult<byte[]> BuildWriteByteCommand(byte station, string address, byte[] value, bool sumCheck = true, byte waitTime = 0)
        {
            OperateResult<string> operateResult = FxAnalysisAddress(address);
            if (!operateResult.IsSuccess)
            {
                return OperateResult.CreateFailedResult<byte[]>(operateResult);
            }
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append(station.ToString("D2"));
            stringBuilder.Append("FF");
            stringBuilder.Append("WW");
            stringBuilder.Append(waitTime.ToString("X"));
            stringBuilder.Append(operateResult.Content);
            stringBuilder.Append((value.Length / 2).ToString("D2"));
            byte[] array = new byte[value.Length * 2];
            for (int i = 0; i < value.Length / 2; i++)
            {
                SoftBasic.BuildAsciiBytesFrom(BitConverter.ToUInt16(value, i * 2)).CopyTo(array, 4 * i);
            }
            stringBuilder.Append(Encoding.ASCII.GetString(array));
            byte[] array2 = null;
            array2 = ((!sumCheck) ? Encoding.ASCII.GetBytes(stringBuilder.ToString()) : Encoding.ASCII.GetBytes(CalculateAcc(stringBuilder.ToString())));
            array2 = SoftBasic.SpliceArray<byte>(new byte[1]
		{
			5
		}, array2);
            return OperateResult.CreateSuccessResult(array2);
        }

        public static OperateResult<byte[]> BuildStart(byte station, bool sumCheck = true, byte waitTime = 0)
        {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append(station.ToString("D2"));
            stringBuilder.Append("FF");
            stringBuilder.Append("RR");
            stringBuilder.Append(waitTime.ToString("X"));
            byte[] array = null;
            array = ((!sumCheck) ? Encoding.ASCII.GetBytes(stringBuilder.ToString()) : Encoding.ASCII.GetBytes(CalculateAcc(stringBuilder.ToString())));
            array = SoftBasic.SpliceArray<byte>(new byte[1]
		{
			5
		}, array);
            return OperateResult.CreateSuccessResult(array);
        }

        public static OperateResult<byte[]> BuildStop(byte station, bool sumCheck = true, byte waitTime = 0)
        {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append(station.ToString("D2"));
            stringBuilder.Append("FF");
            stringBuilder.Append("RS");
            stringBuilder.Append(waitTime.ToString("X"));
            byte[] array = null;
            array = ((!sumCheck) ? Encoding.ASCII.GetBytes(stringBuilder.ToString()) : Encoding.ASCII.GetBytes(CalculateAcc(stringBuilder.ToString())));
            array = SoftBasic.SpliceArray<byte>(new byte[1]
		{
			5
		}, array);
            return OperateResult.CreateSuccessResult(array);
        }

        public static OperateResult<byte[]> BuildReadPlcType(byte station, bool sumCheck = true, byte waitTime = 0)
        {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append(station.ToString("D2"));
            stringBuilder.Append("FF");
            stringBuilder.Append("PC");
            stringBuilder.Append(waitTime.ToString("X"));
            byte[] array = null;
            array = ((!sumCheck) ? Encoding.ASCII.GetBytes(stringBuilder.ToString()) : Encoding.ASCII.GetBytes(CalculateAcc(stringBuilder.ToString())));
            array = SoftBasic.SpliceArray<byte>(new byte[1]
		{
			5
		}, array);
            return OperateResult.CreateSuccessResult(array);
        }

        public static OperateResult<string> GetPlcTypeFromCode(string code)
        {
            switch (code)
            {
                case "F2":
                    return OperateResult.CreateSuccessResult("FX1S");
                case "8E":
                    return OperateResult.CreateSuccessResult("FX0N");
                case "8D":
                    return OperateResult.CreateSuccessResult("FX2/FX2C");
                case "9E":
                    return OperateResult.CreateSuccessResult("FX1N/FX1NC");
                case "9D":
                    return OperateResult.CreateSuccessResult("FX2N/FX2NC");
                case "F4":
                    return OperateResult.CreateSuccessResult("FX3G");
                case "F3":
                    return OperateResult.CreateSuccessResult("FX3U/FX3UC");
                case "98":
                    return OperateResult.CreateSuccessResult("A0J2HCPU");
                case "A1":
                    return OperateResult.CreateSuccessResult("A1CPU /A1NCPU");
                case "A2":
                    return OperateResult.CreateSuccessResult("A2CPU/A2NCPU/A2SCPU");
                case "92":
                    return OperateResult.CreateSuccessResult("A2ACPU");
                case "93":
                    return OperateResult.CreateSuccessResult("A2ACPU-S1");
                case "9A":
                    return OperateResult.CreateSuccessResult("A2CCPU");
                case "82":
                    return OperateResult.CreateSuccessResult("A2USCPU");
                case "83":
                    return OperateResult.CreateSuccessResult("A2CPU-S1/A2USCPU-S1");
                case "A3":
                    return OperateResult.CreateSuccessResult("A3CPU/A3NCPU");
                case "94":
                    return OperateResult.CreateSuccessResult("A3ACPU");
                case "A4":
                    return OperateResult.CreateSuccessResult("A3HCPU/A3MCPU");
                case "84":
                    return OperateResult.CreateSuccessResult("A3UCPU");
                case "85":
                    return OperateResult.CreateSuccessResult("A4UCPU");
                case "AB":
                    return OperateResult.CreateSuccessResult("AJ72P25/R25");
                case "8B":
                    return OperateResult.CreateSuccessResult("AJ72LP25/BR15");
                default:
                    return new OperateResult<string>(StringResources.Language.NotSupportedDataType + " Code:" + code);
            }
        }
    }
}
